#pragma Once

#include <iostream>
#include <memory>
#include <functional>
#include <unordered_map>
#include "Comm.hpp"
#include "Epoller.hpp"
#include "Socket.hpp"
#include "nocopy.hpp"
class Connection;
class TcpServer;

using func_t = std::function<void(std::weak_ptr<Connection>)>; // 回调函数

uint32_t EVENT_IN = (EPOLLIN | EPOLLET);
uint32_t EVENT_OUT = (EPOLLOUT | EPOLLET);

// 由于read函数读取数据是不安全的，所以我们设置一个Connection类用来保证 文件fd读取数据能够完全读完
class Connection
{
public:
    Connection(int sock) : _sock(sock)
    {
    }

    void SetHandler(func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        _recv_cb = recv_cb;
        _send_cb = send_cb;
        _except_cb = except_cb;
    }

    void SetWeakServer(std::weak_ptr<TcpServer> tcp_server_ptr)
    {
        _tcp_server_ptr = tcp_server_ptr;
    }

    int Getfd()
    {
        return _sock;
    }

    void AppendInbuffer(const std::string &info)
    {
        _inbuffer += info;
    }

    void AppendOutbuffer(const std::string &info)
    {
        _outbuffer += info;
    }

    std::string &GetInBuffer()
    {
        return _inbuffer;
    }

    std::string &GetOutBuffer()
    {
        return _outbuffer;
    }

    ~Connection()
    {
    }

private:
    int _sock;
    std::string _inbuffer;  // 我们自己封装的一个文件描述符的输入缓冲区，该缓冲区的数据用于交给上层处理
                            // 但是这个缓冲区是存在问题的，比如它没法接受二进制流
    std::string _outbuffer; // 输出缓冲区
public:
    func_t _recv_cb;   // 接受回调函数
    func_t _send_cb;   // 发送回调函数
    func_t _except_cb; // 异常处理回调函数

    // 添加一个回指指针
    std::weak_ptr<TcpServer> _tcp_server_ptr;
};

// enable_shared_from_this:可以提供返回当前对象的this对应的shared_ptr
class TcpServer : public std::enable_shared_from_this<TcpServer>, public nocopy
{
    static const int num = 64; // struct epoll_event[] 的大小
public:
    TcpServer(uint16_t port, func_t OnMessage)
        : _port(port),
          _LisSock_ptr(new Sock()),
          _epoller_ptr(new Epoller()),
          _OnMessage(OnMessage)
    {
    }

    void Init()
    {
        _LisSock_ptr->Socket();
        SetNonBlockOrDie(_LisSock_ptr->Getfd()); // 设置非堵塞
        _LisSock_ptr->Bind(_port);
        _LisSock_ptr->Listen();
        lg(Info, "listen sucess fd: %d", _LisSock_ptr->Getfd());

        AddConnection(_LisSock_ptr->Getfd(), EVENT_IN,
                      std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);

        lg(Info, "listen AddConnection sucess ");
    }

    void AddConnection(int sock, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb)
    {
        // 1. 给sock建立一个Connection对象
        std::shared_ptr<Connection> new_connection(new Connection(sock));
        new_connection->SetHandler(recv_cb, send_cb, except_cb);
        new_connection->SetWeakServer(shared_from_this()); // shared_from_this(): 返回当前对象的shared_ptr
        // 2. 将这个Connection对象放入到 connenctions容器中
        _connections.insert(std::make_pair(sock, new_connection));
        // 3. 添加对应的事件到epoll 里 rb_tree中，epoller_ctl
        _epoller_ptr->EpollerCtl(EPOLL_CTL_ADD, sock, event);
    }

    // 事件管理器
    void Recver(std::weak_ptr<Connection> conn)
    {
        if (conn.expired())
            return;
        auto connection = conn.lock();
        int sock = connection->Getfd();
        // 由于是ET模型，并且我们设置了非堵塞，因此轮询的将数据读到 sock对应的Connection类的 缓冲区中，保持数据的完整性
        while (true)
        {
            char buffer[128];
            memset(buffer, 0, sizeof(buffer));
            ssize_t n = read(sock, buffer, sizeof(buffer) - 1);
            if (n > 0)
            {
                // 将buffer中的数据读入到Connection中的_inbuffer中
                connection->AppendInbuffer(buffer);
            }
            else if (n == 0)
            {
                lg(Info, "sockfd: %d, client quit.....", sock);
                connection->_except_cb(connection);
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK) // 当接收缓冲区中没有数据可读时
                    break;
                else if (errno == EINTR) // 指操作被信号中断唤醒，需要重新读/写
                    continue;
                else
                {
                    lg(Warning, "sockfd: %d,  read error...", sock);
                    connection->_except_cb(connection);
                    break;
                }
            }
        }

        // 将数据再上层进行处理
        _OnMessage(connection);
    }

    void Sender(std::weak_ptr<Connection> conn)
    {
        if (conn.expired())
            return;
        auto connection = conn.lock();
        int sock = connection->Getfd();
        auto &outbuffer = connection->GetOutBuffer();
        while (true)
        {
            ssize_t n = write(sock, outbuffer.c_str(), outbuffer.size());
            if (n > 0)
            {
                if (outbuffer.empty())
                    break;
                outbuffer.erase(0, n);
            }
            else if (n == 0)
            {
                // 在非阻塞套接字中，若对端关闭连接且发送缓冲区已满，某些系统可能返回0（但更常见返回-1并设errno=EPIPE）
                return;
            }
            else
            {
                if (errno == EWOULDBLOCK) // 当接收缓冲区中没有数据可读时
                    break;
                else if (errno == EINTR) // 指操作被信号中断唤醒，需要重新读/写
                    continue;
                else
                {
                    lg(Warning, "sockfd: %d, write error...", sock);
                    connection->_except_cb(connection);
                    break;
                }
            }
        }

        // 实际上写事件是比较少调用，且失败的概率是比较低的，所以我们需要及时的对写事件的关心做处理
        if (!outbuffer.empty())
        {
            // 开启对写事件的关心
            EnableEvent(connection->Getfd(), true, true);
        }
        else
        {
            // 关闭对写事件的关心
            EnableEvent(connection->Getfd(), true, false);
        }
    }

    // 修改对写事件的关系
    void EnableEvent(int sock, bool read_able, bool write_able)
    {

        uint32_t events = 0;
        events |= ((read_able ? EPOLLIN : 0) | (write_able ? EPOLLOUT : 0) | EPOLLET);
        _epoller_ptr->EpollerCtl(EPOLL_CTL_MOD, sock, events);
    }

    void Excepter(std::weak_ptr<Connection> conn)
    {
        if (conn.expired())
            return;
        auto connection = conn.lock();
        int sock = connection->Getfd();

        // 1. 先移除对特定事件的关心
        _epoller_ptr->EpollerCtl(EPOLL_CTL_DEL, sock, 0);
        // 2. 关闭异常的文件描述符
        close(sock);
        // 3. 从unorder_map中移除
        _connections.erase(sock);

        lg(Info, "Except finish....");
    }

    // listen就绪处理器
    void Accepter(std::weak_ptr<Connection> conn)
    {
        // weak_ptr 的 lock 函数用于创建一个新的 shared_ptr 对象，该对象共享被管理对象的所有权。
        // 如果 weak_ptr 指向的对象已经被释放，则返回一个空的 shared_ptr
        auto connection = conn.lock();
        while (true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sock = ::accept(connection->Getfd(), (struct sockaddr *)&peer, &len);
            if (sock > 0)
            {
                // 设置nonblock
                SetNonBlockOrDie(sock);
                // 调用AddConnection
                AddConnection(sock, EVENT_IN,
                              std::bind(&TcpServer::Recver, this, std::placeholders::_1),
                              std::bind(&TcpServer::Sender, this, std::placeholders::_1),
                              std::bind(&TcpServer::Excepter, this, std::placeholders::_1));

                lg(Info, "get a new link, fd: %d", connection->Getfd());
            }
            else
            {
                if (errno == EWOULDBLOCK) // 当接收缓冲区中没有数据可读时
                    break;
                else if (errno == EINTR) // 指操作被信号中断唤醒，需要重新读/写
                    continue;
                else
                    break;
            }
        }
    }

    bool IsConnectionSafe(int fd)
    {
        auto iter = _connections.find(fd);
        if (iter == _connections.end())
            return false;
        else
            return true;
    }

    void Dispatcher(int n)
    {
        for (int i = 0; i < n; i++)
        {
            uint32_t events = revs[i].events;
            int sock = revs[i].data.fd;
            if ((events & EPOLLIN) && IsConnectionSafe(sock))
            {
                // 执行recv事件
                if (_connections[sock]->_recv_cb)
                    _connections[sock]->_recv_cb(_connections[sock]);
            }
            else if ((events & EPOLLOUT) && IsConnectionSafe(sock))
            {
                // 执行send事件
                if (_connections[sock]->_send_cb)
                {
                    _connections[sock]->_send_cb(_connections[sock]);
                }
            }
        }
    }

    void Start()
    {

        while (true)
        {
            // epoll_wait
            int n = _epoller_ptr->EpollerWait(revs, num, 3000);
            if (n > 0)
            {
                // 有事件就绪
                lg(Debug, "event happened ....");
                Dispatcher(n);
            }
            else if (n == 0)
            {
                lg(Info, "time out ...");
            }
            else
            {
                lg(Error, "epll wait error:%s", strerror(errno));
            }
        }
    }

    ~TcpServer()
    {
        _LisSock_ptr->Close();
    }

private:
    std::shared_ptr<Sock> _LisSock_ptr;    // 监听套接字
    std::shared_ptr<Epoller> _epoller_ptr; // epoll
    uint16_t _port;                        // 端口
    struct epoll_event revs[num];
    std::unordered_map<int, std::shared_ptr<Connection>> _connections; // 建立fd，和Connection的联系

    // 让上层处理信息
    func_t _OnMessage;
};